﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Common
{
    //让子弹的脚本实现IResetable接口；每次创建对象池创建对象，都调用IResetable接口方法，达到逻辑（计算目标点）重置的目的
    interface IResetable
    {
        void OnRest();
    }
	/// <summary>
    /// 对象池
    /// 避免物体频繁创建和销毁
    /// </summary>
	public class GameObjectPool : MonoSingleton<GameObjectPool> {
        private Dictionary<string, List<GameObject>> cache;

        private void Awake()
        {
            cache = new Dictionary<string, List<GameObject>>();
        }
        //弃用，菜鸟写法，需要重构
        //public GameObject CreateObject(string key, GameObject prefab, Vector3 pos, Quaternion dir)
        //{
        //    //在池中查找禁用的物体
        //    GameObject go = null;
        //    if (cache.ContainsKey(key))
        //        go = cache[key].Find(o => !o.activeInHierarchy);
        //    if (go != null)
        //    {
        //        go.transform.position = pos;
        //        go.transform.rotation = dir;
        //        go.SetActive(true);
        //    }
        //    else
        //    {
        //        go = Instantiate(prefab, pos, dir);
        //        if (!cache.ContainsKey(key)) cache.Add(key, new List<GameObject>());
        //        cache[key].Add(go);
        //    }
        //    return go;
        //}

        /// <summary>
        /// 使用对象池创建对象
        /// </summary>
        /// <param name="key">对象种类</param>
        /// <param name="prefab">对象预制件</param>
        /// <param name="pos">位置</param>
        /// <param name="dir">方向</param>
        /// <returns></returns>
        public GameObject CreateObject(string key, GameObject prefab, Vector3 pos, Quaternion dir)
        {
            //1.在池中查找可以使用的对象
            GameObject go = FindUsableObject(key);
            //2.如果没有找到，则创建加入池中
            if (go == null)
            {
                go = Instantiate(prefab);
                Add(key, go);
            }
            //3.使用对象
            UseObject(pos, dir, go);
            return go;
        }
        
        private void UseObject(Vector3 pos, Quaternion dir, GameObject go)
        {
            go.transform.position = pos;
            //print("pos" + pos);
            go.transform.rotation = dir;
            //print("dir" + dir);
            go.SetActive(true);
            //先确定枪口位置方向，再确定子弹目标位置，如果go.GetComponent<Bullet>().CalculateTargetPosition();则违背开放封闭原则，弃用
            //虚方法 抽象方法//不合适不是一个概念 弃用；委托//不是1对多的关系 弃用
            //接口方法 
            //go.GetComponent<IResetable>().OnRest();//如果有多个脚本多个逻辑变化,修改代码如下
            //遍历物体中所有需要重置的脚本
            foreach (var item in go.GetComponents<IResetable>())
            {
                item.OnRest();
            }
        }

        private void Add(string key, GameObject go)
        {
            if (!cache.ContainsKey(key))
            {
                cache.Add(key, new List<GameObject>());
            }
            cache[key].Add(go);//key可能没有
        }

        private GameObject FindUsableObject(string key)
        {
            //字典如果存在当前记录，则在集合中查找禁用的物体
            if (cache.ContainsKey(key))
            {//在池中查找禁用的物体
                return cache[key].Find(o => !o.activeInHierarchy);
                //for (int i = 0; i < cache[key].Count; i++)
                //{
                //    if (!cache[key][i].activeInHierarchy)
                //    {
                //    }
                //}
            }
            return null;
        }

        /// <summary>
        /// 立即回收
        /// </summary>
        /// <param name="go"></param>
        public void CollectObject(GameObject go)
        {
            go.SetActive(false);
        }
        /// <summary>
        /// 延迟回收
        /// </summary>
        /// <param name="go"></param>
        /// <param name="delay"></param>
        public void CollectObject(GameObject go,float delay)
        {
            StartCoroutine(DelayCollect(go,delay));
        }
        private IEnumerator DelayCollect(GameObject go, float delay)
        {
            yield return new WaitForSeconds(delay);
            CollectObject(go);
        }
        public void Clear(string key)
        {
            //****此处为自加代码，注意****
            if (!cache.ContainsKey(key))
            {
                print("无该键值对象");
                return;
            }
            //释放游戏对象

            foreach (var item in cache[key])
            {
                Destroy(item);
            }
            //移除键
            cache.Remove(key);
        }
        public void ClearAll()
        {
            //Bug1：异常——无效的操作
            //foreach (var key in cache.Keys)//foreach只读
            //{
            //    Clear(key);
            //}
            //cache.Clear();

            //方法弃用
            //string[] keys = new string[cache.Keys.Count];
            //cache.Keys.CopyTo(keys, 0);
            //for (int i = 0; i < keys.Length; i++)
            //{
            //    Clear(keys[i]);
            //}

            List<string> keyList = new List<string>(cache.Keys);
            foreach (var item in keyList)
            {
                //遍历集合 移除字典记录
                Clear(item);
            }
            //Bug2:子弹不能沿枪的正前方发射//业务逻辑的错误
            //调试:每次创建对象池创建对象，都调用IResetable接口方法，达到逻辑（计算目标点）重置的目的
        }
    }
}

